#include "lib/3d/xmath.h"
#include "lib/3d/pvm3demulation.h"
#include "lib/drivers/pvmdeviceinterface.h"
#include "lib/pvmlib.h"

#include "lib/memory/pvmmemory.h"

#ifndef PVM3DSOFTWARENAMESPACE
#error "PVM3DSOFTWARENAMESPACE not defined"
#endif

namespace PVM3DSOFTWARENAMESPACE
{

#if	defined(SCALAR_FIXEDPOINT)
#define P3DListenerClassName	P3DListenerFixed
#define P3DSourceClassName		P3DSourceFixed
#else
#define P3DListenerClassName	P3DListenerFloat
#define P3DSourceClassName		P3DSourceFloat
#endif

class P3DListenerClassName;
class P3DSourceClassName;

class P3DListenerClassName :  public PVMAllocableObject
{
public:
	PVMDEVICE device;

	P3DSourceClassName* sources;
	pvm_int32 maxSources;

	pvm_int32 distanceModel;

	Vector position;
	Vector velocity;
	Vector front;
	Vector top;
	pvmlib::scalar	dopplerFactor;
    pvmlib::scalar dopplerVelocity;
    pvmlib::scalar speedOfSound;
	pvmlib::scalar	unitSize;

	pvm_bool dirty;
	pvm_bool enabled;

	Vector realPosition;
	Vector realVelocity;
	Vector normalFront;
	Vector normalTop;
	Vector normalRight;

	int deviceChanelsNum;
public:
	P3DListenerClassName(PVMDEVICE deviceHandle, pvm_int32 sourcesNum);
	~P3DListenerClassName();

	void Update();
};

class P3DSourceClassName : public PVMAllocableObject
{
public:
	Vector position;
	Vector velocity;
	pvmlib::scalar minDist;
	pvmlib::scalar maxDist;
	pvmlib::scalar	rolloffFactor;

	pvm_int32 sourceRate;//source rate
	pvm_int32 maxVolume;
	pvm_int32 minVolume;
	pvm_bool stopOnDistance;

//
	pvm_bool dirty;//show that data should be updated
	pvm_bool enabled;
	pvm_bool listenerRelative;

	Vector realRelativePosition;
	Vector realVelocity;
	pvmlib::scalar realMinDist;
	pvmlib::scalar realMaxDist;

	pvm_int32 realVolume;
	pvm_int32 realPanning;
	pvm_int32 realRate;

	pvm_bool updateRealParams;

public:
	P3DSourceClassName();
};

////////////////////////////////////////
P3DListenerClassName::P3DListenerClassName(PVMDEVICE deviceHandle, pvm_int32 sourcesNum)
{
	device = deviceHandle;

	sources = new P3DSourceClassName[sourcesNum];
	if(sources!=0)
		maxSources = sourcesNum;
	else
		maxSources = 0;

	distanceModel = E_INVERSE_DISTANCE_CLAMPED;

	position.reset();
	velocity.reset();
	front.set(pvmlib::SCALAR_ZERO, pvmlib::SCALAR_ZERO, -pvmlib::SCALAR_ONE);
	top.set(pvmlib::SCALAR_ZERO, pvmlib::SCALAR_ONE, pvmlib::SCALAR_ZERO);
	dopplerFactor = pvmlib::SCALAR_ONE;
    dopplerVelocity = pvmlib::SCALAR_ONE;
    speedOfSound = f2s(343.3f);//meters per second
	unitSize = pvmlib::SCALAR_ONE;

	dirty = true;
	enabled = true;

	deviceChanelsNum = -1;
}

P3DListenerClassName::~P3DListenerClassName()
{
	if(sources)
		delete[]( sources);
}

P3DSourceClassName::P3DSourceClassName()
{
	position.reset();
	velocity.reset();
	minDist = pvmlib::SCALAR_ONE;
	maxDist = pvmlib::SCALAR_MAX;
	rolloffFactor = pvmlib::SCALAR_ONE;

	sourceRate = 100;
	maxVolume = 256;
	minVolume = 0;
	stopOnDistance = false;

//
	dirty = true;//show that data should be updated
	enabled = false;
	listenerRelative = false;
};

void* P3DCreateListener(PVMDEVICE device)
{
	if(!device)
		return NULL;
	if((((IPVMDeviceBase*)device)->GetDeviceType()&EPVM_DTYPE_OUTPUT)==0)
		return NULL;

	pvm_uint32 maxChanels = ((IPVMDeviceOut*)device)->GetMaxVoices();

	return new P3DListenerClassName(device, maxChanels);
}

void P3DDestroyListener(void* li)
{
	if(li==NULL)
		return;

	delete( ((P3DListenerClassName*)li));
}

PVMDEVICE P3DGetDevice(void* li)
{
	if(li==NULL)
		return NULL;

	return ((P3DListenerClassName*)li)->device;
}

pvm_bool P3DDisableListener(void* li)
{
	if(li==NULL)
		return false;

	((P3DListenerClassName*)li)->enabled = false;

	return true;
}

pvm_bool P3DEnableListener(void* li)
{
	if(li==NULL)
		return false;

	((P3DListenerClassName*)li)->enabled = true;

	return true;
}

pvm_bool P3DSetDistanceModel(void* li, pvm_int32 model)
{
	if(li==NULL)
		return false;

	((P3DListenerClassName*)li)->enabled = true;

	if(E_LINEAR_DISTANCE!=model &&
		E_LINEAR_DISTANCE_CLAMPED!=model &&
		E_INVERSE_DISTANCE!=model &&
		E_INVERSE_DISTANCE_CLAMPED!=model &&
		E_EXPONENT_DISTANCE!=model &&
		E_EXPONENT_DISTANCE_CLAMPED!=model)
		return false;

	((P3DListenerClassName*)li)->distanceModel = model;

	return true;
}

pvm_bool P3DGetDistanceModel(void* li, pvm_int32* model)
{
	if(li==NULL||model==NULL)
		return false;

	*model = ((P3DListenerClassName*)li)->distanceModel;

	return true;
}

pvm_int32 P3DGetDistanceModelsSupport()
{
	return E_LINEAR_DISTANCE | E_LINEAR_DISTANCE_CLAMPED |
		E_INVERSE_DISTANCE | E_INVERSE_DISTANCE_CLAMPED |
		E_EXPONENT_DISTANCE | E_EXPONENT_DISTANCE_CLAMPED;
}

pvm_bool P3DSetListenerPosition(void* li, pvmlib::scalar x, pvmlib::scalar y, pvmlib::scalar z)
{
	if(li==NULL)
		return false;

	((P3DListenerClassName*)li)->position.set(x, y, z);
	((P3DListenerClassName*)li)->dirty = true;

	return true;
}

pvm_bool P3DSetListenerVelocity(void* li, pvmlib::scalar x, pvmlib::scalar y, pvmlib::scalar z)
{
	if(li==NULL)
		return false;

	((P3DListenerClassName*)li)->velocity.set(x, y, z);
	((P3DListenerClassName*)li)->dirty = true;

	return true;
}

pvm_bool P3DSetListenerOrientation(void* li, pvmlib::scalar xFront, pvmlib::scalar yFront, pvmlib::scalar zFront, pvmlib::scalar xTop, pvmlib::scalar yTop, pvmlib::scalar zTop)
{
	if(li==NULL)
		return false;
	
	((P3DListenerClassName*)li)->front.set(xFront, yFront, zFront);
	((P3DListenerClassName*)li)->top.set(xTop, yTop, zTop);
	((P3DListenerClassName*)li)->dirty = true;

	return true;
}

pvm_bool P3DSetDopplerFactor(void* li, pvmlib::scalar factor)
{
	if(li==NULL)
		return false;
	
	((P3DListenerClassName*)li)->dopplerFactor = factor;
	((P3DListenerClassName*)li)->dirty = true;

	return true;
}

pvm_bool P3DSetUnitSize(void* li, pvmlib::scalar size)
{
	if(li==NULL)
		return false;
	
	((P3DListenerClassName*)li)->unitSize = size;
	((P3DListenerClassName*)li)->dirty = true;

	return true;
}

pvm_bool P3DGetListenerPosition(void* li, pvmlib::scalar* x, pvmlib::scalar* y, pvmlib::scalar* z)
{
	if(li==NULL || x==NULL || y==NULL || z==NULL)
		return false;

	*x = ((P3DListenerClassName*)li)->position.x;
	*y = ((P3DListenerClassName*)li)->position.y;
	*z = ((P3DListenerClassName*)li)->position.z;

	return true;
}

pvm_bool P3DGetListenerVelocity(void* li, pvmlib::scalar* x, pvmlib::scalar* y, pvmlib::scalar* z)
{
	if(li==NULL || x==NULL || y==NULL || z==NULL)
		return false;

	*x = ((P3DListenerClassName*)li)->velocity.x;
	*y = ((P3DListenerClassName*)li)->velocity.y;
	*z = ((P3DListenerClassName*)li)->velocity.z;

	return true;
}

pvm_bool P3DGetListenerOrientation(void* li, pvmlib::scalar* xFront, pvmlib::scalar* yFront, pvmlib::scalar* zFront, pvmlib::scalar* xTop, pvmlib::scalar* yTop, pvmlib::scalar* zTop)
{
	if(li==NULL || xFront==NULL || yFront==NULL || zFront==NULL || 
		xTop==NULL || yTop==NULL || zTop==NULL)
		return false;

	*xFront = ((P3DListenerClassName*)li)->front.x;
	*yFront = ((P3DListenerClassName*)li)->front.y;
	*zFront = ((P3DListenerClassName*)li)->front.z;

	*xTop = ((P3DListenerClassName*)li)->top.x;
	*yTop = ((P3DListenerClassName*)li)->top.y;
	*zTop = ((P3DListenerClassName*)li)->top.z;

	return true;
}

pvm_bool P3DGetDopplerFactor(void* li, pvmlib::scalar* factor)
{
	if(li==NULL || factor==NULL)
		return false;

	*factor = ((P3DListenerClassName*)li)->dopplerFactor;

	return true;
}

pvm_bool P3DGetUnitSize(void* li, pvmlib::scalar* size)
{
	if(li==NULL || size==NULL)
		return false;

	*size = ((P3DListenerClassName*)li)->unitSize;

	return true;
}

pvm_bool P3DControlEnable(void* li, pvm_int32 chanelNum)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].enabled = true;
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DControlDisable(void* li, pvm_int32 chanelNum)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].enabled = false;
	return true;
}

pvm_bool P3DIsControlEnabled(void* li, pvm_int32 chanelNum)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	return ((P3DListenerClassName*)li)->sources[chanelNum].enabled;
}

pvm_bool P3DSetVoicePosition(void* li, pvm_int32 chanelNum, pvmlib::scalar x, pvmlib::scalar y, pvmlib::scalar z)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].position.set(x, y, z);
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DSetVoiceVelocity(void* li, pvm_int32 chanelNum, pvmlib::scalar x, pvmlib::scalar y, pvmlib::scalar z)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].velocity.set(x, y, z);
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DSetVoiceDirection(void* /*li*/, pvm_int32 /*chanelNum*/, pvmlib::scalar /*x*/, pvmlib::scalar /*y*/, pvmlib::scalar /*z*/)
{
	return false;//not supported
}

pvm_bool P3DSetVoiceMinDistance(void* li, pvm_int32 chanelNum, pvmlib::scalar d)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].minDist = d;
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DSetVoiceMaxDistance(void* li, pvm_int32 chanelNum, pvmlib::scalar d)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].maxDist = d;
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DSetRolloffFactor(void* li, pvm_int32 chanelNum, pvmlib::scalar factor)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].rolloffFactor = factor;
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DSetStopOnDistance(void* li, pvm_int32 chanelNum, pvm_bool flag)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].stopOnDistance = flag;
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DSetVoiceRate(void* li, pvm_int32 chanelNum, pvm_int32 value)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].sourceRate = value;
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DSetVoiceMaxVolume(void* li, pvm_int32 chanelNum, pvm_int32 value)
{
	if(li==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;

	if(value>256||value<0)
		return false;

	((P3DListenerClassName*)li)->sources[chanelNum].maxVolume = value;
	((P3DListenerClassName*)li)->sources[chanelNum].dirty = true;

	return true;
}

pvm_bool P3DGetVoicePosition(void* li, pvm_int32 chanelNum, pvmlib::scalar* x, pvmlib::scalar* y, pvmlib::scalar* z)
{
	if(li==NULL || x==NULL || y==NULL || z==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;


	*x = ((P3DListenerClassName*)li)->sources[chanelNum].position.x;
	*y = ((P3DListenerClassName*)li)->sources[chanelNum].position.y;
	*z = ((P3DListenerClassName*)li)->sources[chanelNum].position.z;

	return true;
}

pvm_bool P3DGetVoiceVelocity(void* li, pvm_int32 chanelNum, pvmlib::scalar* x, pvmlib::scalar* y, pvmlib::scalar* z)
{
	if(li==NULL || x==NULL || y==NULL || z==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;


	*x = ((P3DListenerClassName*)li)->sources[chanelNum].velocity.x;
	*y = ((P3DListenerClassName*)li)->sources[chanelNum].velocity.y;
	*z = ((P3DListenerClassName*)li)->sources[chanelNum].velocity.z;

	return true;
}


pvm_bool P3DGetVoiceDirection(void* /*li*/, pvm_int32 /*chanelNum*/, pvmlib::scalar* /*x*/, pvmlib::scalar* /*y*/, pvmlib::scalar* /*z*/)
{
	return false;
}

pvm_bool P3DGetVoiceMinDistance(void* li, pvm_int32 chanelNum, pvmlib::scalar* d)
{
	if(li==NULL || d==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;


	*d = ((P3DListenerClassName*)li)->sources[chanelNum].minDist;

	return true;
}

pvm_bool P3DGetVoiceMaxDistance(void* li, pvm_int32 chanelNum, pvmlib::scalar* d)
{
	if(li==NULL || d==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;


	*d = ((P3DListenerClassName*)li)->sources[chanelNum].maxDist;

	return true;
}

pvm_bool P3DGetRolloffFactor(void* li, pvm_int32 chanelNum, pvmlib::scalar* factor)
{
	if(li==NULL || factor==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;


	*factor = ((P3DListenerClassName*)li)->sources[chanelNum].rolloffFactor;

	return true;
}

pvm_bool P3DGetStopOnDistance(void* li, pvm_int32 chanelNum, pvm_bool* flag)
{
	if(li==NULL || flag==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;


	*flag = ((P3DListenerClassName*)li)->sources[chanelNum].stopOnDistance;

	return true;
}

pvm_bool P3DGetVoiceRate(void* li, pvm_int32 chanelNum, pvm_int32* value)
{
	if(li==NULL || value==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;


	*value = ((P3DListenerClassName*)li)->sources[chanelNum].sourceRate;

	return true;
}

pvm_bool P3DGetVoiceMaxVolume(void* li, pvm_int32 chanelNum, pvm_int32* value)
{
	if(li==NULL || value==NULL)
		return false;

	if(chanelNum>=((P3DListenerClassName*)li)->maxSources||chanelNum<0)
		return false;


	*value = ((P3DListenerClassName*)li)->sources[chanelNum].maxVolume;

	return true;
}


void P3DUpdate(void* li)
{
	if(li==NULL)
		return;

	((P3DListenerClassName*)li)->Update();
}

void P3DListenerClassName::Update()
{
 	if(enabled==false)
		return;

	if(device==NULL)
		return;

	Vector toSrc;
	Vector sourceToListener;

	if(deviceChanelsNum<=0)
		deviceChanelsNum = PVMDeviceOutGetChanelsNum(device);

	if(dirty)
	{
		if(unitSize==pvmlib::SCALAR_ONE)
		{
			realPosition = position;
			realVelocity = velocity;
		}
		else
		{
			realPosition = position*unitSize;
			realVelocity = velocity*unitSize;
		}

		normalFront = front;
		normalTop = top;
		normalFront.normalize();
		normalTop.normalize();
		normalRight = normalFront.crossProduct(normalTop);
	}

	bool hasEnabledSources = false;
	bool needUpdateParams = false;

	for(pvm_int32 i=0; i<maxSources; i++)
	{
		sources[i].updateRealParams = false;
		if(false==sources[i].enabled)
			continue;

		hasEnabledSources = true;

		if(false==sources[i].dirty && false==dirty)
			continue;

		if(PVMIsVoiceFree(device, i))
			continue;

		bool isStopped = false;
		P3DSourceClassName* src =  &sources[i];

		if(unitSize==pvmlib::SCALAR_ONE)
		{
			if(src->listenerRelative)
				src->realRelativePosition = src->position;
			else
				src->realRelativePosition = src->position - realPosition;

			src->realVelocity = src->velocity;
			src->realMinDist = src->minDist;
			src->realMaxDist = src->maxDist;
		}
		else
		{
			if(src->listenerRelative)
				src->realRelativePosition = src->position*unitSize;
			else
				src->realRelativePosition = src->position*unitSize - realPosition;

			src->realVelocity = src->velocity*unitSize;
			src->realMinDist = src->minDist*unitSize;
			src->realMaxDist = src->maxDist;
			if(src->realMaxDist<pvmlib::SCALAR_MAX)
				src->realMaxDist *= unitSize;
		}

		//volume attenuation on distance
		if(src->rolloffFactor!=pvmlib::SCALAR_ZERO)
		{
			pvmlib::scalar attenuation = pvmlib::SCALAR_ONE;
			pvmlib::scalar distance = pvmlib::SCALAR_ZERO;

            if(src->maxDist>=src->minDist)
			{
				distance = src->realRelativePosition.getLength();

				switch(distanceModel)
				{
				case E_LINEAR_DISTANCE_CLAMPED:
					distance=maxs(distance, src->realMinDist);
					distance=mins(distance, src->realMaxDist);
					if(src->realMaxDist<src->realMinDist)
						break;
				case E_LINEAR_DISTANCE:
					distance=mins(distance, src->realMaxDist);
					if (src->realMaxDist != src->realMinDist)
					{
						attenuation = pvmlib::SCALAR_ONE - (src->rolloffFactor*(distance - src->realMinDist)/(src->realMaxDist - src->realMinDist));
					}
					break;

				case E_INVERSE_DISTANCE_CLAMPED:
					distance=maxs(distance, src->realMinDist);
					distance=mins(distance, src->realMaxDist);
					if(src->realMaxDist<src->realMinDist)
						break;
				case E_INVERSE_DISTANCE:
					if (src->realMinDist > pvmlib::SCALAR_ZERO)
					{
						if ((src->realMinDist + (src->rolloffFactor * (distance - src->realMinDist))) > pvmlib::SCALAR_ZERO)
							attenuation = src->realMinDist / (src->realMinDist + (src->rolloffFactor * (distance - src->realMinDist)));
					}
					break;

				case E_EXPONENT_DISTANCE_CLAMPED:
					distance = maxs(distance, src->realMinDist);
					distance = mins(distance, src->realMaxDist);
					if(src->realMaxDist<src->realMinDist)
						break;
				case E_EXPONENT_DISTANCE:
					if((distance > pvmlib::SCALAR_ZERO) && (src->realMinDist > pvmlib::SCALAR_ZERO))
					{
						attenuation = pows(distance/src->realMinDist, -src->rolloffFactor);
					}
					break;
				}
			}

			src->realVolume = (int)(n2s(src->maxVolume) * attenuation);

			if(src->realVolume==0 && src->stopOnDistance)
			{
				PVMVoiceStop(device, i);
				isStopped = true;
			}
		}
		else
		{
			src->realVolume = src->maxVolume;
		}

		//doppler
		if(dopplerFactor!=pvmlib::SCALAR_ZERO)
		{
			sourceToListener = -src->realRelativePosition;
			sourceToListener.normalize();

            pvmlib::scalar flVSS, flVLS = pvmlib::SCALAR_ZERO;

            if(src->listenerRelative==false)
                flVLS = realVelocity.dotProduct(sourceToListener);
            flVSS = src->realVelocity.dotProduct(sourceToListener);

            pvmlib::scalar flMaxVelocity = (dopplerVelocity * speedOfSound) / dopplerFactor;

            if (flVSS >= flMaxVelocity)
                flVSS = (flMaxVelocity - pvmlib::SCALAR_ONE);
            else if (flVSS <= -flMaxVelocity)
                flVSS = -flMaxVelocity + pvmlib::SCALAR_ONE;

            if (flVLS >= flMaxVelocity)
                flVLS = (flMaxVelocity - pvmlib::SCALAR_ONE);
            else if (flVLS <= -flMaxVelocity)
                flVLS = -flMaxVelocity + pvmlib::SCALAR_ONE;

            src->realRate = (int)(n2s(src->sourceRate)*
                       ((speedOfSound * dopplerVelocity) - (dopplerFactor * flVLS)) /
                       ((speedOfSound * dopplerVelocity) - (dopplerFactor * flVSS)));
		}
		else
		{
			src->realRate = src->sourceRate;
		}

		if(deviceChanelsNum>1)
		{
			//volume panning on position
			//projecting voice source to plane
			//A(x-x0) + B(y-y0) + C(z-z0)=0
			//x0, y0, z0 are equal to 0
			//Ax + By + Cz = 0
			//normal vector N(A, B, C)
			pvmlib::scalar t;
			//xs,ys,zs - coordiates of source
			//finding t for line equation - x = At + xs, y = Bt + ys, z = Ct + zs
			t = -(normalTop.x * src->realRelativePosition.x + normalTop.y * src->realRelativePosition.y + normalTop.z * src->realRelativePosition.z);
			pvmlib::scalar projX, projY, projZ;

			projX = normalTop.x*t + src->realRelativePosition.x;
			projY = normalTop.y*t + src->realRelativePosition.y;
			projZ = normalTop.z*t + src->realRelativePosition.z;

			toSrc.set(projX, projY, projZ);
			toSrc.normalize();

			src->realPanning = (int)(n2s(256)*toSrc.dotProduct(normalRight));//get cosine of vector
			pvmlib::scalar frontCos = toSrc.dotProduct(normalFront);
			if(frontCos<0 && src->realVolume>0)
			{
				pvmlib::scalar sVol = n2s(src->realVolume);
				src->realVolume = (int)(sVol + sVol*frontCos*pvmlib::SCALAR_HALF);
			}
		}

		if(!isStopped)
		{
			src->updateRealParams = true;
			needUpdateParams = true;
		}

		sources[i].dirty = false;

	}

	if(needUpdateParams && PVMVoiceBeginBatchParams(device))
	{
		for(pvm_int32 i=0; i<maxSources; i++)
		{
			if(sources[i].updateRealParams)
			{
				PVMVoiceSetPanningVolumeRateDirect(device, i, sources[i].realPanning, sources[i].realVolume, sources[i].realRate);
			}
		}
		PVMVoiceEndBatchParams(device);
	}

	dirty = false;
}

}
